PHP 實戰留言板


Posted by tzutzu858 on 2020-09-07

規劃產品路由與功能

檔案 功能
index.php 觀看所有留言
handle_add_comment.php 處理新增留言
conn.php 連接資料庫
handle_login.php 處理登入邏輯
handle_register.php 處理註冊邏輯
login.php 登入頁面
logout.php 登出
register.php 註冊頁面
utils.php 重複用的 function 放這

規劃資料結構以及建置資料庫

三個資料表 :

資料表 功用 欄位
users 會員資料 1.id 2.nickname 3.username 4.password 5.created_at
comments 留言資料 1.id 2.nickname 3.content 4.created_at
tokens token 對照 username 1.id 2.token 3.username

id 記得都要設 A_I AUTO_INCREMENT 自動累加
如果使用 php 內建的 session 可以不用 tokens 那張資料表


切板完串接資料庫顯示留言

conn.php

$conn = new mysqli($server_name, $username, $password, $db_name);
// conn 為 connection 的簡寫,第一個參數是 server 名稱,第二個是帳號,第三個是密碼,第四個是 database
if ($conn->connect_error) { // 物件存取屬性是用 -> 來表示
    die('資料庫連線錯誤:' . $conn->connect_error);
}
$conn->query('SET NAMES UTF8'); // 設定編碼
$conn->query('SET time_zone = "+8:00"'); // 設定台灣時間

index.php
顯示留言板面

$result = $conn->query("SELECT * FROM comments ORDER BY id DESC");
if (!$result) {
    die('Error:' . $conn->error);
}

在 HTML 想要安插的地方放暱稱、建立時間、內容

while ($row = $result->fetch_assoc()) {
<?php echo $row['nickname']; ?>
<?php echo $row['created_at'] ?>
<?php echo $row['content'] ?>

加入新增留言功能

$_POST 取資料放入資料庫
handle_add_comment.php

require_once 'conn.php';
if (empty($_POST['content'])) {
    die('資料不齊全');
}
$content = $_POST['content'];
$sql = sprintf("INSERT INTO comments(content) VALUE('%s')", $content);
$result = $conn->query($sql);
if (!$result) {
    die($conn->error);
}
header("Location: index.php");

實作註冊功能

在 index.php 放個連結
<a href="register.php">註冊</a>
登入、登出以此類推

register.php 頁面長得跟 index.php 差不多,所以拿 index.php 來改一改就好
要有暱稱、帳號、密碼框供使用者填入

handle_register.php : 將註冊資料寫入資料庫,長得跟 handle_add_comment.php 很像,所以複製改一改就好,username 在資料表設唯一,並且做檢查
MYSQL ERROR CODE 錯誤編號的意義
1062:欄位值重複,入庫失敗

if (!$result) {
    $code = $conn->errno;
    if ($code === 1062) {
        header('Location: register.php?errCode=2');
    }
    die($conn->error);
}

handle_register.php

require_once 'conn.php';

if (empty($_POST['nickname']) || empty($_POST['username']) || empty($_POST['password'])) {
    die('資料不齊全');
}

$nickname = $_POST['nickname'];
$username = $_POST['username'];
$password = $_POST['password'];

$sql = sprintf("INSERT INTO users(nickname, username, password) VALUE('%s', '%s', '%s')", $nickname, $username, $password);
$result = $conn->query($sql);
if (!$result) {
    $code = $conn->errno;
    if ($code === 1062) {
        header('Location: register.php?errCode=2');
    }
    die($conn->error);
}

header("Location: index.php");

實作登入功能

login.php 和 register.php 頁面長差不多,複製拿來改一改
handle_login.php 和 handle_register.php 處理邏輯也差不多,複製拿來改一改

num_rows 代表結果有多少筆資料
如果輸入錯誤 帶參數回去 header("Location: login.php?errCode=2");
在 login.php 找個地方做判斷,並顯示帳號密碼有誤出來

 <?php
if (!empty($_GET['errCode'])) {
    $code = $_GET['errCode'];
    $msg = 'Error';
    if ($code === '2') {
        $msg = '帳號密碼有誤';
    }
    echo '<h3 class="error">錯誤 : ' . $msg . '</h3>';
}
?>

handle_login.php

<?php
require_once 'conn.php';

if (empty($_POST['username']) || empty($_POST['password'])) {
    die('資料不齊全');
}

$username = $_POST['username'];
$password = $_POST['password'];

$sql = sprintf("SELECT * FROM users WHERE username='%s' and password='%s'", $username, $password);
$result = $conn->query($sql);
if (!$result) {
    die($conn->error);
}
if ($result->num_rows) {
    $result = $conn->query($sql);
    if (!$result) {
        die($conn->error);
    }    
    header("Location: index.php");
} else {
    header("Location: login.php?errCode=2");
}

該怎麼記住登入狀態?Cookie 簡介與實作

HTTP 狀態健忘特性
如何看 Cookie : 打開 DevTools --> application --> 左邊版面 storage 有 cookies 可以看,其實就是 name 跟 value
瀏覽器會自動把符合條件的 Cookie 帶上來,符合條件指的是沒有過期,domain 和路徑要符合
所以在處理登入頁面的 php ,handle_login.php

$token = generateToken();
$expire = time() + 3600 * 24 * 30;
setcookie("token", $token, $expire);
header("Location: index.php");

utils.php

function generateToken()
{
    $s = '';
    for ($i = 1; $i <= 16; $i++) {
        $s .= chr(rand(65, 90));
    }
    return $s;
}

在 handle_login.php 裡面 setcookie 後,在 index.php 要拿到用 $_COOKIE

$username = null;
if (!empty($_COOKIE['token'])) {
    $user = getUserFromToken($_COOKIE['token']);
    $username = $user['username'];
}

登出就把 cookie 清空

require_once "conn.php";
$token = $_COOKIE['token'];
$sql = sprintf("DELETE FROM tokens WHERE token = '%s'", $token);
$conn->query($sql);
setcookie("token", "", 0);
header("Location: index.php");

handle_login.php

require_once 'conn.php';
require_once 'utils.php';

if (empty($_POST['username']) || empty($_POST['password'])) {
    die('資料不齊全');
}

$username = $_POST['username'];
$password = $_POST['password'];

$sql = sprintf("SELECT * FROM users WHERE username='%s' and password='%s'", $username, $password);
$result = $conn->query($sql);
if (!$result) {
    die($conn->error);
}
if ($result->num_rows) {
    // 建立 token 並儲存
    $token = generateToken();
    $sql = sprintf(
        "INSERT INTO tokens(token,username) VALUES('%s', '%s')",$token, $username
    );
    $result = $conn->query($sql);
    if (!$result) {
        die($conn->error);
    }    
    // 登入成功
    $expire = time() + 3600 * 24 * 30;
    setcookie("token", $token, $expire);
    header("Location: index.php");
} else {
    header("Location: login.php?errCode=2");
}

發布留言的顯示暱稱
所以在 handle_add_comment.php

if (!empty($_COOKIE['token'])) {
    $user = getUserFromToken($_COOKIE['token']);
    $username = $user['username'];
}

utils.php

function getUserFromToken($token)
{
    global $conn;
    $sql = sprintf("SELECT username FROM tokens WHERE token = '%s'", $token);
    $result = $conn->query($sql);
    $row = $result->fetch_assoc();
    $username = $row['username'];

    $sql = sprintf("SELECT * FROM users WHERE username = '%s'", $username);
    $result = $conn->query($sql);
    $row = $result->fetch_assoc();
    return $row; // username, id, nickname
}









Related Posts

COSCUP 2021 截圖王

COSCUP 2021 截圖王

Countdown Clock

Countdown Clock

1731. The Number of Employees Which Report to Each Employee

1731. The Number of Employees Which Report to Each Employee


Comments